home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / lib / python2.6 / mhlib.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-11-11  |  33.2 KB  |  1,245 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. """MH interface -- purely object-oriented (well, almost)
  5.  
  6. Executive summary:
  7.  
  8. import mhlib
  9.  
  10. mh = mhlib.MH()         # use default mailbox directory and profile
  11. mh = mhlib.MH(mailbox)  # override mailbox location (default from profile)
  12. mh = mhlib.MH(mailbox, profile) # override mailbox and profile
  13.  
  14. mh.error(format, ...)   # print error message -- can be overridden
  15. s = mh.getprofile(key)  # profile entry (None if not set)
  16. path = mh.getpath()     # mailbox pathname
  17. name = mh.getcontext()  # name of current folder
  18. mh.setcontext(name)     # set name of current folder
  19.  
  20. list = mh.listfolders() # names of top-level folders
  21. list = mh.listallfolders() # names of all folders, including subfolders
  22. list = mh.listsubfolders(name) # direct subfolders of given folder
  23. list = mh.listallsubfolders(name) # all subfolders of given folder
  24.  
  25. mh.makefolder(name)     # create new folder
  26. mh.deletefolder(name)   # delete folder -- must have no subfolders
  27.  
  28. f = mh.openfolder(name) # new open folder object
  29.  
  30. f.error(format, ...)    # same as mh.error(format, ...)
  31. path = f.getfullname()  # folder's full pathname
  32. path = f.getsequencesfilename() # full pathname of folder's sequences file
  33. path = f.getmessagefilename(n)  # full pathname of message n in folder
  34.  
  35. list = f.listmessages() # list of messages in folder (as numbers)
  36. n = f.getcurrent()      # get current message
  37. f.setcurrent(n)         # set current message
  38. list = f.parsesequence(seq)     # parse msgs syntax into list of messages
  39. n = f.getlast()         # get last message (0 if no messagse)
  40. f.setlast(n)            # set last message (internal use only)
  41.  
  42. dict = f.getsequences() # dictionary of sequences in folder {name: list}
  43. f.putsequences(dict)    # write sequences back to folder
  44.  
  45. f.createmessage(n, fp)  # add message from file f as number n
  46. f.removemessages(list)  # remove messages in list from folder
  47. f.refilemessages(list, tofolder) # move messages in list to other folder
  48. f.movemessage(n, tofolder, ton)  # move one message to a given destination
  49. f.copymessage(n, tofolder, ton)  # copy one message to a given destination
  50.  
  51. m = f.openmessage(n)    # new open message object (costs a file descriptor)
  52. m is a derived class of mimetools.Message(rfc822.Message), with:
  53. s = m.getheadertext()   # text of message's headers
  54. s = m.getheadertext(pred) # text of message's headers, filtered by pred
  55. s = m.getbodytext()     # text of message's body, decoded
  56. s = m.getbodytext(0)    # text of message's body, not decoded
  57. """
  58. from warnings import warnpy3k
  59. warnpy3k('the mhlib module has been removed in Python 3.0; use the mailbox module instead', stacklevel = 2)
  60. del warnpy3k
  61. MH_PROFILE = '~/.mh_profile'
  62. PATH = '~/Mail'
  63. MH_SEQUENCES = '.mh_sequences'
  64. FOLDER_PROTECT = 448
  65. import os
  66. import sys
  67. import re
  68. import mimetools
  69. import multifile
  70. import shutil
  71. from bisect import bisect
  72. __all__ = [
  73.     'MH',
  74.     'Error',
  75.     'Folder',
  76.     'Message']
  77.  
  78. class Error(Exception):
  79.     pass
  80.  
  81.  
  82. class MH:
  83.     '''Class representing a particular collection of folders.
  84.     Optional constructor arguments are the pathname for the directory
  85.     containing the collection, and the MH profile to use.
  86.     If either is omitted or empty a default is used; the default
  87.     directory is taken from the MH profile if it is specified there.'''
  88.     
  89.     def __init__(self, path = None, profile = None):
  90.         '''Constructor.'''
  91.         if profile is None:
  92.             profile = MH_PROFILE
  93.         
  94.         self.profile = os.path.expanduser(profile)
  95.         if path is None:
  96.             path = self.getprofile('Path')
  97.         
  98.         if not path:
  99.             path = PATH
  100.         
  101.         if not os.path.isabs(path) and path[0] != '~':
  102.             path = os.path.join('~', path)
  103.         
  104.         path = os.path.expanduser(path)
  105.         if not os.path.isdir(path):
  106.             raise Error, 'MH() path not found'
  107.         os.path.isdir(path)
  108.         self.path = path
  109.  
  110.     
  111.     def __repr__(self):
  112.         '''String representation.'''
  113.         return 'MH(%r, %r)' % (self.path, self.profile)
  114.  
  115.     
  116.     def error(self, msg, *args):
  117.         '''Routine to print an error.  May be overridden by a derived class.'''
  118.         sys.stderr.write('MH error: %s\n' % msg % args)
  119.  
  120.     
  121.     def getprofile(self, key):
  122.         '''Return a profile entry, None if not found.'''
  123.         return pickline(self.profile, key)
  124.  
  125.     
  126.     def getpath(self):
  127.         """Return the path (the name of the collection's directory)."""
  128.         return self.path
  129.  
  130.     
  131.     def getcontext(self):
  132.         '''Return the name of the current folder.'''
  133.         context = pickline(os.path.join(self.getpath(), 'context'), 'Current-Folder')
  134.         if not context:
  135.             context = 'inbox'
  136.         
  137.         return context
  138.  
  139.     
  140.     def setcontext(self, context):
  141.         '''Set the name of the current folder.'''
  142.         fn = os.path.join(self.getpath(), 'context')
  143.         f = open(fn, 'w')
  144.         f.write('Current-Folder: %s\n' % context)
  145.         f.close()
  146.  
  147.     
  148.     def listfolders(self):
  149.         '''Return the names of the top-level folders.'''
  150.         folders = []
  151.         path = self.getpath()
  152.         for name in os.listdir(path):
  153.             fullname = os.path.join(path, name)
  154.             if os.path.isdir(fullname):
  155.                 folders.append(name)
  156.                 continue
  157.         
  158.         folders.sort()
  159.         return folders
  160.  
  161.     
  162.     def listsubfolders(self, name):
  163.         '''Return the names of the subfolders in a given folder
  164.         (prefixed with the given folder name).'''
  165.         fullname = os.path.join(self.path, name)
  166.         nlinks = os.stat(fullname).st_nlink
  167.         if nlinks <= 2:
  168.             return []
  169.         subfolders = []
  170.         subnames = os.listdir(fullname)
  171.         for subname in subnames:
  172.             fullsubname = os.path.join(fullname, subname)
  173.             if os.path.isdir(fullsubname):
  174.                 name_subname = os.path.join(name, subname)
  175.                 subfolders.append(name_subname)
  176.                 nlinks = nlinks - 1
  177.                 if nlinks <= 2:
  178.                     break
  179.                 
  180.             nlinks <= 2
  181.         
  182.         subfolders.sort()
  183.         return subfolders
  184.  
  185.     
  186.     def listallfolders(self):
  187.         '''Return the names of all folders and subfolders, recursively.'''
  188.         return self.listallsubfolders('')
  189.  
  190.     
  191.     def listallsubfolders(self, name):
  192.         '''Return the names of subfolders in a given folder, recursively.'''
  193.         fullname = os.path.join(self.path, name)
  194.         nlinks = os.stat(fullname).st_nlink
  195.         if nlinks <= 2:
  196.             return []
  197.         subfolders = []
  198.         subnames = os.listdir(fullname)
  199.         for subname in subnames:
  200.             if subname[0] == ',' or isnumeric(subname):
  201.                 continue
  202.             
  203.             fullsubname = os.path.join(fullname, subname)
  204.             if os.path.isdir(fullsubname):
  205.                 name_subname = os.path.join(name, subname)
  206.                 subfolders.append(name_subname)
  207.                 if not os.path.islink(fullsubname):
  208.                     subsubfolders = self.listallsubfolders(name_subname)
  209.                     subfolders = subfolders + subsubfolders
  210.                 
  211.                 nlinks = nlinks - 1
  212.                 if nlinks <= 2:
  213.                     break
  214.                 
  215.             nlinks <= 2
  216.         
  217.         subfolders.sort()
  218.         return subfolders
  219.  
  220.     
  221.     def openfolder(self, name):
  222.         '''Return a new Folder object for the named folder.'''
  223.         return Folder(self, name)
  224.  
  225.     
  226.     def makefolder(self, name):
  227.         '''Create a new folder (or raise os.error if it cannot be created).'''
  228.         protect = pickline(self.profile, 'Folder-Protect')
  229.         if protect and isnumeric(protect):
  230.             mode = int(protect, 8)
  231.         else:
  232.             mode = FOLDER_PROTECT
  233.         os.mkdir(os.path.join(self.getpath(), name), mode)
  234.  
  235.     
  236.     def deletefolder(self, name):
  237.         '''Delete a folder.  This removes files in the folder but not
  238.         subdirectories.  Raise os.error if deleting the folder itself fails.'''
  239.         fullname = os.path.join(self.getpath(), name)
  240.         for subname in os.listdir(fullname):
  241.             fullsubname = os.path.join(fullname, subname)
  242.             
  243.             try:
  244.                 os.unlink(fullsubname)
  245.             continue
  246.             except os.error:
  247.                 self.error('%s not deleted, continuing...' % fullsubname)
  248.                 continue
  249.             
  250.  
  251.         
  252.         os.rmdir(fullname)
  253.  
  254.  
  255. numericprog = re.compile('^[1-9][0-9]*$')
  256.  
  257. def isnumeric(str):
  258.     return numericprog.match(str) is not None
  259.  
  260.  
  261. class Folder:
  262.     '''Class representing a particular folder.'''
  263.     
  264.     def __init__(self, mh, name):
  265.         '''Constructor.'''
  266.         self.mh = mh
  267.         self.name = name
  268.         if not os.path.isdir(self.getfullname()):
  269.             raise Error, 'no folder %s' % name
  270.         os.path.isdir(self.getfullname())
  271.  
  272.     
  273.     def __repr__(self):
  274.         '''String representation.'''
  275.         return 'Folder(%r, %r)' % (self.mh, self.name)
  276.  
  277.     
  278.     def error(self, *args):
  279.         '''Error message handler.'''
  280.         self.mh.error(*args)
  281.  
  282.     
  283.     def getfullname(self):
  284.         '''Return the full pathname of the folder.'''
  285.         return os.path.join(self.mh.path, self.name)
  286.  
  287.     
  288.     def getsequencesfilename(self):
  289.         """Return the full pathname of the folder's sequences file."""
  290.         return os.path.join(self.getfullname(), MH_SEQUENCES)
  291.  
  292.     
  293.     def getmessagefilename(self, n):
  294.         '''Return the full pathname of a message in the folder.'''
  295.         return os.path.join(self.getfullname(), str(n))
  296.  
  297.     
  298.     def listsubfolders(self):
  299.         '''Return list of direct subfolders.'''
  300.         return self.mh.listsubfolders(self.name)
  301.  
  302.     
  303.     def listallsubfolders(self):
  304.         '''Return list of all subfolders.'''
  305.         return self.mh.listallsubfolders(self.name)
  306.  
  307.     
  308.     def listmessages(self):
  309.         '''Return the list of messages currently present in the folder.
  310.         As a side effect, set self.last to the last message (or 0).'''
  311.         messages = []
  312.         match = numericprog.match
  313.         append = messages.append
  314.         for name in os.listdir(self.getfullname()):
  315.             if match(name):
  316.                 append(name)
  317.                 continue
  318.         
  319.         messages = map(int, messages)
  320.         messages.sort()
  321.         if messages:
  322.             self.last = messages[-1]
  323.         else:
  324.             self.last = 0
  325.         return messages
  326.  
  327.     
  328.     def getsequences(self):
  329.         '''Return the set of sequences for the folder.'''
  330.         sequences = { }
  331.         fullname = self.getsequencesfilename()
  332.         
  333.         try:
  334.             f = open(fullname, 'r')
  335.         except IOError:
  336.             return sequences
  337.  
  338.         while None:
  339.             line = f.readline()
  340.             if not line:
  341.                 break
  342.             
  343.             fields = line.split(':')
  344.             if len(fields) != 2:
  345.                 self.error('bad sequence in %s: %s' % (fullname, line.strip()))
  346.             
  347.             key = fields[0].strip()
  348.             value = IntSet(fields[1].strip(), ' ').tolist()
  349.             sequences[key] = value
  350.             continue
  351.             return sequences
  352.  
  353.     
  354.     def putsequences(self, sequences):
  355.         '''Write the set of sequences back to the folder.'''
  356.         fullname = self.getsequencesfilename()
  357.         f = None
  358.         for key, seq in sequences.iteritems():
  359.             s = IntSet('', ' ')
  360.             s.fromlist(seq)
  361.             if not f:
  362.                 f = open(fullname, 'w')
  363.             
  364.             f.write('%s: %s\n' % (key, s.tostring()))
  365.         
  366.         if not f:
  367.             
  368.             try:
  369.                 os.unlink(fullname)
  370.             except os.error:
  371.                 pass
  372.             except:
  373.                 None<EXCEPTION MATCH>os.error
  374.             
  375.  
  376.         None<EXCEPTION MATCH>os.error
  377.         f.close()
  378.  
  379.     
  380.     def getcurrent(self):
  381.         '''Return the current message.  Raise Error when there is none.'''
  382.         seqs = self.getsequences()
  383.         
  384.         try:
  385.             return max(seqs['cur'])
  386.         except (ValueError, KeyError):
  387.             raise Error, 'no cur message'
  388.  
  389.  
  390.     
  391.     def setcurrent(self, n):
  392.         '''Set the current message.'''
  393.         updateline(self.getsequencesfilename(), 'cur', str(n), 0)
  394.  
  395.     
  396.     def parsesequence(self, seq):
  397.         '''Parse an MH sequence specification into a message list.
  398.         Attempt to mimic mh-sequence(5) as close as possible.
  399.         Also attempt to mimic observed behavior regarding which
  400.         conditions cause which error messages.'''
  401.         all = self.listmessages()
  402.         if not all:
  403.             raise Error, 'no messages in %s' % self.name
  404.         all
  405.         if seq == 'all':
  406.             return all
  407.         i = seq.find(':')
  408.         if i >= 0:
  409.             head = seq[:i]
  410.             dir = ''
  411.             tail = seq[i + 1:]
  412.             if tail[:1] in '-+':
  413.                 dir = tail[:1]
  414.                 tail = tail[1:]
  415.             
  416.             if not isnumeric(tail):
  417.                 raise Error, 'bad message list %s' % seq
  418.             isnumeric(tail)
  419.             
  420.             try:
  421.                 count = int(tail)
  422.             except (ValueError, OverflowError):
  423.                 count = len(all)
  424.  
  425.             
  426.             try:
  427.                 anchor = self._parseindex(head, all)
  428.             except Error:
  429.                 msg = None
  430.                 seqs = self.getsequences()
  431.                 if head not in seqs:
  432.                     if not msg:
  433.                         msg = 'bad message list %s' % seq
  434.                     
  435.                     raise Error, msg, sys.exc_info()[2]
  436.                 head not in seqs
  437.                 msgs = seqs[head]
  438.                 if not msgs:
  439.                     raise Error, 'sequence %s empty' % head
  440.                 msgs
  441.                 if dir == '-':
  442.                     return msgs[-count:]
  443.                 return msgs[:count]
  444.  
  445.             if not dir:
  446.                 if head in ('prev', 'last'):
  447.                     dir = '-'
  448.                 
  449.             
  450.             if dir == '-':
  451.                 i = bisect(all, anchor)
  452.                 return all[max(0, i - count):i]
  453.             i = bisect(all, anchor - 1)
  454.             return all[i:i + count]
  455.         i >= 0
  456.         i = seq.find('-')
  457.         if i >= 0:
  458.             begin = self._parseindex(seq[:i], all)
  459.             end = self._parseindex(seq[i + 1:], all)
  460.             i = bisect(all, begin - 1)
  461.             j = bisect(all, end)
  462.             r = all[i:j]
  463.             if not r:
  464.                 raise Error, 'bad message list %s' % seq
  465.             r
  466.             return r
  467.         
  468.         try:
  469.             n = self._parseindex(seq, all)
  470.         except Error:
  471.             i >= 0
  472.             msg = i >= 0
  473.             seq == 'all'
  474.             seqs = self.getsequences()
  475.             if seq not in seqs:
  476.                 if not msg:
  477.                     msg = 'bad message list %s' % seq
  478.                 
  479.                 raise Error, msg
  480.             seq not in seqs
  481.             return seqs[seq]
  482.  
  483.         if n not in all:
  484.             if isnumeric(seq):
  485.                 raise Error, "message %d doesn't exist" % n
  486.             isnumeric(seq)
  487.             raise Error, 'no %s message' % seq
  488.         n not in all
  489.         return [
  490.             n]
  491.  
  492.     
  493.     def _parseindex(self, seq, all):
  494.         '''Internal: parse a message number (or cur, first, etc.).'''
  495.         if isnumeric(seq):
  496.             
  497.             try:
  498.                 return int(seq)
  499.             except (OverflowError, ValueError):
  500.                 return sys.maxint
  501.             
  502.  
  503.         None<EXCEPTION MATCH>(OverflowError, ValueError)
  504.         if seq in ('cur', '.'):
  505.             return self.getcurrent()
  506.         if seq == 'first':
  507.             return all[0]
  508.         if seq == 'last':
  509.             return all[-1]
  510.         if seq == 'next':
  511.             n = self.getcurrent()
  512.             i = bisect(all, n)
  513.             
  514.             try:
  515.                 return all[i]
  516.             except IndexError:
  517.                 seq == 'last'
  518.                 seq == 'last'
  519.                 seq == 'first'
  520.                 raise Error, 'no next message'
  521.             except:
  522.                 seq == 'last'<EXCEPTION MATCH>IndexError
  523.             
  524.  
  525.         seq == 'last'
  526.         if seq == 'prev':
  527.             n = self.getcurrent()
  528.             i = bisect(all, n - 1)
  529.             if i == 0:
  530.                 raise Error, 'no prev message'
  531.             i == 0
  532.             
  533.             try:
  534.                 return all[i - 1]
  535.             except IndexError:
  536.                 seq == 'last'
  537.                 seq == 'last'
  538.                 seq == 'first'
  539.                 raise Error, 'no prev message'
  540.             except:
  541.                 seq == 'last'<EXCEPTION MATCH>IndexError
  542.             
  543.  
  544.         seq == 'last'
  545.         raise Error, None
  546.  
  547.     
  548.     def openmessage(self, n):
  549.         '''Open a message -- returns a Message object.'''
  550.         return Message(self, n)
  551.  
  552.     
  553.     def removemessages(self, list):
  554.         '''Remove one or more messages -- may raise os.error.'''
  555.         errors = []
  556.         deleted = []
  557.         for n in list:
  558.             path = self.getmessagefilename(n)
  559.             commapath = self.getmessagefilename(',' + str(n))
  560.             
  561.             try:
  562.                 os.unlink(commapath)
  563.             except os.error:
  564.                 pass
  565.  
  566.             
  567.             try:
  568.                 os.rename(path, commapath)
  569.             except os.error:
  570.                 msg = None
  571.                 errors.append(msg)
  572.                 continue
  573.  
  574.             deleted.append(n)
  575.         
  576.         if deleted:
  577.             self.removefromallsequences(deleted)
  578.         
  579.         if errors:
  580.             if len(errors) == 1:
  581.                 raise os.error, errors[0]
  582.             len(errors) == 1
  583.             raise os.error, ('multiple errors:', errors)
  584.         errors
  585.  
  586.     
  587.     def refilemessages(self, list, tofolder, keepsequences = 0):
  588.         """Refile one or more messages -- may raise os.error.
  589.         'tofolder' is an open folder object."""
  590.         errors = []
  591.         refiled = { }
  592.         for n in list:
  593.             ton = tofolder.getlast() + 1
  594.             path = self.getmessagefilename(n)
  595.             topath = tofolder.getmessagefilename(ton)
  596.             
  597.             try:
  598.                 os.rename(path, topath)
  599.             except os.error:
  600.                 
  601.                 try:
  602.                     shutil.copy2(path, topath)
  603.                     os.unlink(path)
  604.                 except (IOError, os.error):
  605.                     msg = None
  606.                     errors.append(msg)
  607.                     
  608.                     try:
  609.                         os.unlink(topath)
  610.                     continue
  611.                     except os.error:
  612.                         continue
  613.                     
  614.  
  615.                 except:
  616.                     None<EXCEPTION MATCH>(IOError, os.error)
  617.                 
  618.  
  619.                 None<EXCEPTION MATCH>(IOError, os.error)
  620.  
  621.             tofolder.setlast(ton)
  622.             refiled[n] = ton
  623.         
  624.         if refiled:
  625.             if keepsequences:
  626.                 tofolder._copysequences(self, refiled.items())
  627.             
  628.             self.removefromallsequences(refiled.keys())
  629.         
  630.         if errors:
  631.             if len(errors) == 1:
  632.                 raise os.error, errors[0]
  633.             len(errors) == 1
  634.             raise os.error, ('multiple errors:', errors)
  635.         errors
  636.  
  637.     
  638.     def _copysequences(self, fromfolder, refileditems):
  639.         '''Helper for refilemessages() to copy sequences.'''
  640.         fromsequences = fromfolder.getsequences()
  641.         tosequences = self.getsequences()
  642.         changed = 0
  643.         for name, seq in fromsequences.items():
  644.             
  645.             try:
  646.                 toseq = tosequences[name]
  647.                 new = 0
  648.             except KeyError:
  649.                 toseq = []
  650.                 new = 1
  651.  
  652.             for fromn, ton in refileditems:
  653.                 if fromn in seq:
  654.                     toseq.append(ton)
  655.                     changed = 1
  656.                     continue
  657.             
  658.             if new and toseq:
  659.                 tosequences[name] = toseq
  660.                 continue
  661.         
  662.         if changed:
  663.             self.putsequences(tosequences)
  664.         
  665.  
  666.     
  667.     def movemessage(self, n, tofolder, ton):
  668.         '''Move one message over a specific destination message,
  669.         which may or may not already exist.'''
  670.         path = self.getmessagefilename(n)
  671.         f = open(path)
  672.         f.close()
  673.         del f
  674.         topath = tofolder.getmessagefilename(ton)
  675.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  676.         
  677.         try:
  678.             os.rename(topath, backuptopath)
  679.         except os.error:
  680.             pass
  681.  
  682.         
  683.         try:
  684.             os.rename(path, topath)
  685.         except os.error:
  686.             ok = 0
  687.             
  688.             try:
  689.                 tofolder.setlast(None)
  690.                 shutil.copy2(path, topath)
  691.                 ok = 1
  692.             finally:
  693.                 if not ok:
  694.                     
  695.                     try:
  696.                         os.unlink(topath)
  697.                     except os.error:
  698.                         pass
  699.                     except:
  700.                         None<EXCEPTION MATCH>os.error
  701.                     
  702.  
  703.  
  704.             os.unlink(path)
  705.  
  706.         self.removefromallsequences([
  707.             n])
  708.  
  709.     
  710.     def copymessage(self, n, tofolder, ton):
  711.         '''Copy one message over a specific destination message,
  712.         which may or may not already exist.'''
  713.         path = self.getmessagefilename(n)
  714.         f = open(path)
  715.         f.close()
  716.         del f
  717.         topath = tofolder.getmessagefilename(ton)
  718.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  719.         
  720.         try:
  721.             os.rename(topath, backuptopath)
  722.         except os.error:
  723.             pass
  724.  
  725.         ok = 0
  726.         
  727.         try:
  728.             tofolder.setlast(None)
  729.             shutil.copy2(path, topath)
  730.             ok = 1
  731.         finally:
  732.             if not ok:
  733.                 
  734.                 try:
  735.                     os.unlink(topath)
  736.                 except os.error:
  737.                     pass
  738.                 except:
  739.                     None<EXCEPTION MATCH>os.error
  740.                 
  741.  
  742.  
  743.  
  744.     
  745.     def createmessage(self, n, txt):
  746.         '''Create a message, with text from the open file txt.'''
  747.         path = self.getmessagefilename(n)
  748.         backuppath = self.getmessagefilename(',%d' % n)
  749.         
  750.         try:
  751.             os.rename(path, backuppath)
  752.         except os.error:
  753.             pass
  754.  
  755.         ok = 0
  756.         BUFSIZE = 16384
  757.         
  758.         try:
  759.             f = open(path, 'w')
  760.             while None:
  761.                 buf = txt.read(BUFSIZE)
  762.                 if not buf:
  763.                     break
  764.                 
  765.                 continue
  766.                 f.close()
  767.                 ok = 1
  768.             if not ok:
  769.                 
  770.                 try:
  771.                     os.unlink(path)
  772.                 except os.error:
  773.                     pass
  774.                 except:
  775.                     None<EXCEPTION MATCH>os.error
  776.                 
  777.  
  778.             return None
  779.  
  780.  
  781.     
  782.     def removefromallsequences(self, list):
  783.         """Remove one or more messages from all sequences (including last)
  784.         -- but not from 'cur'!!!"""
  785.         if hasattr(self, 'last') and self.last in list:
  786.             del self.last
  787.         
  788.         sequences = self.getsequences()
  789.         changed = 0
  790.         for name, seq in sequences.items():
  791.             if name == 'cur':
  792.                 continue
  793.             
  794.             for n in list:
  795.                 if n in seq:
  796.                     seq.remove(n)
  797.                     changed = 1
  798.                     if not seq:
  799.                         del sequences[name]
  800.                     
  801.                 seq
  802.             
  803.         
  804.         if changed:
  805.             self.putsequences(sequences)
  806.         
  807.  
  808.     
  809.     def getlast(self):
  810.         '''Return the last message number.'''
  811.         if not hasattr(self, 'last'):
  812.             self.listmessages()
  813.         
  814.         return self.last
  815.  
  816.     
  817.     def setlast(self, last):
  818.         '''Set the last message number.'''
  819.         if last is None:
  820.             if hasattr(self, 'last'):
  821.                 del self.last
  822.             
  823.         else:
  824.             self.last = last
  825.  
  826.  
  827.  
  828. class Message(mimetools.Message):
  829.     
  830.     def __init__(self, f, n, fp = None):
  831.         '''Constructor.'''
  832.         self.folder = f
  833.         self.number = n
  834.         if fp is None:
  835.             path = f.getmessagefilename(n)
  836.             fp = open(path, 'r')
  837.         
  838.         mimetools.Message.__init__(self, fp)
  839.  
  840.     
  841.     def __repr__(self):
  842.         '''String representation.'''
  843.         return 'Message(%s, %s)' % (repr(self.folder), self.number)
  844.  
  845.     
  846.     def getheadertext(self, pred = None):
  847.         """Return the message's header text as a string.  If an
  848.         argument is specified, it is used as a filter predicate to
  849.         decide which headers to return (its argument is the header
  850.         name converted to lower case)."""
  851.         if pred is None:
  852.             return ''.join(self.headers)
  853.         headers = []
  854.         hit = 0
  855.         for line in self.headers:
  856.             if not line[0].isspace():
  857.                 i = line.find(':')
  858.                 if i > 0:
  859.                     hit = pred(line[:i].lower())
  860.                 
  861.             
  862.             if hit:
  863.                 headers.append(line)
  864.                 continue
  865.         
  866.         return ''.join(headers)
  867.  
  868.     
  869.     def getbodytext(self, decode = 1):
  870.         """Return the message's body text as string.  This undoes a
  871.         Content-Transfer-Encoding, but does not interpret other MIME
  872.         features (e.g. multipart messages).  To suppress decoding,
  873.         pass 0 as an argument."""
  874.         self.fp.seek(self.startofbody)
  875.         encoding = self.getencoding()
  876.         if not decode or encoding in ('', '7bit', '8bit', 'binary'):
  877.             return self.fp.read()
  878.         
  879.         try:
  880.             StringIO = StringIO
  881.             import cStringIO
  882.         except ImportError:
  883.             encoding in ('', '7bit', '8bit', 'binary')
  884.             encoding in ('', '7bit', '8bit', 'binary')
  885.             StringIO = StringIO
  886.             import StringIO
  887.         except:
  888.             encoding in ('', '7bit', '8bit', 'binary')
  889.  
  890.         output = StringIO()
  891.         mimetools.decode(self.fp, output, encoding)
  892.         return output.getvalue()
  893.  
  894.     
  895.     def getbodyparts(self):
  896.         """Only for multipart messages: return the message's body as a
  897.         list of SubMessage objects.  Each submessage object behaves
  898.         (almost) as a Message object."""
  899.         if self.getmaintype() != 'multipart':
  900.             raise Error, 'Content-Type is not multipart/*'
  901.         self.getmaintype() != 'multipart'
  902.         bdry = self.getparam('boundary')
  903.         if not bdry:
  904.             raise Error, 'multipart/* without boundary param'
  905.         bdry
  906.         self.fp.seek(self.startofbody)
  907.         mf = multifile.MultiFile(self.fp)
  908.         mf.push(bdry)
  909.         parts = []
  910.         while mf.next():
  911.             n = '%s.%r' % (self.number, 1 + len(parts))
  912.             part = SubMessage(self.folder, n, mf)
  913.             parts.append(part)
  914.         mf.pop()
  915.         return parts
  916.  
  917.     
  918.     def getbody(self):
  919.         '''Return body, either a string or a list of messages.'''
  920.         if self.getmaintype() == 'multipart':
  921.             return self.getbodyparts()
  922.         return self.getbodytext()
  923.  
  924.  
  925.  
  926. class SubMessage(Message):
  927.     
  928.     def __init__(self, f, n, fp):
  929.         '''Constructor.'''
  930.         Message.__init__(self, f, n, fp)
  931.         if self.getmaintype() == 'multipart':
  932.             self.body = Message.getbodyparts(self)
  933.         else:
  934.             self.body = Message.getbodytext(self)
  935.         self.bodyencoded = Message.getbodytext(self, decode = 0)
  936.  
  937.     
  938.     def __repr__(self):
  939.         '''String representation.'''
  940.         f = self.folder
  941.         n = self.number
  942.         fp = self.fp
  943.         return 'SubMessage(%s, %s, %s)' % (f, n, fp)
  944.  
  945.     
  946.     def getbodytext(self, decode = 1):
  947.         if not decode:
  948.             return self.bodyencoded
  949.         if type(self.body) == type(''):
  950.             return self.body
  951.  
  952.     
  953.     def getbodyparts(self):
  954.         if type(self.body) == type([]):
  955.             return self.body
  956.  
  957.     
  958.     def getbody(self):
  959.         return self.body
  960.  
  961.  
  962.  
  963. class IntSet:
  964.     """Class implementing sets of integers.
  965.  
  966.     This is an efficient representation for sets consisting of several
  967.     continuous ranges, e.g. 1-100,200-400,402-1000 is represented
  968.     internally as a list of three pairs: [(1,100), (200,400),
  969.     (402,1000)].  The internal representation is always kept normalized.
  970.  
  971.     The constructor has up to three arguments:
  972.     - the string used to initialize the set (default ''),
  973.     - the separator between ranges (default ',')
  974.     - the separator between begin and end of a range (default '-')
  975.     The separators must be strings (not regexprs) and should be different.
  976.  
  977.     The tostring() function yields a string that can be passed to another
  978.     IntSet constructor; __repr__() is a valid IntSet constructor itself.
  979.     """
  980.     
  981.     def __init__(self, data = None, sep = ',', rng = '-'):
  982.         self.pairs = []
  983.         self.sep = sep
  984.         self.rng = rng
  985.         if data:
  986.             self.fromstring(data)
  987.         
  988.  
  989.     
  990.     def reset(self):
  991.         self.pairs = []
  992.  
  993.     
  994.     def __cmp__(self, other):
  995.         return cmp(self.pairs, other.pairs)
  996.  
  997.     
  998.     def __hash__(self):
  999.         return hash(self.pairs)
  1000.  
  1001.     
  1002.     def __repr__(self):
  1003.         return 'IntSet(%r, %r, %r)' % (self.tostring(), self.sep, self.rng)
  1004.  
  1005.     
  1006.     def normalize(self):
  1007.         self.pairs.sort()
  1008.         i = 1
  1009.         while i < len(self.pairs):
  1010.             (alo, ahi) = self.pairs[i - 1]
  1011.             (blo, bhi) = self.pairs[i]
  1012.             if ahi >= blo - 1:
  1013.                 self.pairs[i - 1:i + 1] = [
  1014.                     (alo, max(ahi, bhi))]
  1015.                 continue
  1016.             i = i + 1
  1017.  
  1018.     
  1019.     def tostring(self):
  1020.         s = ''
  1021.         for lo, hi in self.pairs:
  1022.             if lo == hi:
  1023.                 t = repr(lo)
  1024.             else:
  1025.                 t = repr(lo) + self.rng + repr(hi)
  1026.             if s:
  1027.                 s = s + self.sep + t
  1028.                 continue
  1029.             s = t
  1030.         
  1031.         return s
  1032.  
  1033.     
  1034.     def tolist(self):
  1035.         l = []
  1036.         for lo, hi in self.pairs:
  1037.             m = range(lo, hi + 1)
  1038.             l = l + m
  1039.         
  1040.         return l
  1041.  
  1042.     
  1043.     def fromlist(self, list):
  1044.         for i in list:
  1045.             self.append(i)
  1046.         
  1047.  
  1048.     
  1049.     def clone(self):
  1050.         new = IntSet()
  1051.         new.pairs = self.pairs[:]
  1052.         return new
  1053.  
  1054.     
  1055.     def min(self):
  1056.         return self.pairs[0][0]
  1057.  
  1058.     
  1059.     def max(self):
  1060.         return self.pairs[-1][-1]
  1061.  
  1062.     
  1063.     def contains(self, x):
  1064.         for lo, hi in self.pairs:
  1065.             if x <= x:
  1066.                 pass
  1067.             elif x <= hi:
  1068.                 return True
  1069.         
  1070.         return False
  1071.  
  1072.     
  1073.     def append(self, x):
  1074.         for i in range(len(self.pairs)):
  1075.             (lo, hi) = self.pairs[i]
  1076.             if x < lo:
  1077.                 if x + 1 == lo:
  1078.                     self.pairs[i] = (x, hi)
  1079.                 else:
  1080.                     self.pairs.insert(i, (x, x))
  1081.                 if i > 0 and x - 1 == self.pairs[i - 1][1]:
  1082.                     self.pairs[i - 1:i + 1] = [
  1083.                         (self.pairs[i - 1][0], self.pairs[i][1])]
  1084.                 
  1085.                 return None
  1086.             if x <= hi:
  1087.                 return None
  1088.         
  1089.         i = len(self.pairs) - 1
  1090.         self.pairs.append((x, x))
  1091.  
  1092.     
  1093.     def addpair(self, xlo, xhi):
  1094.         if xlo > xhi:
  1095.             return None
  1096.         self.pairs.append((xlo, xhi))
  1097.         self.normalize()
  1098.  
  1099.     
  1100.     def fromstring(self, data):
  1101.         new = []
  1102.         for part in data.split(self.sep):
  1103.             list = []
  1104.             for subp in part.split(self.rng):
  1105.                 s = subp.strip()
  1106.                 list.append(int(s))
  1107.             
  1108.             if len(list) == 1:
  1109.                 new.append((list[0], list[0]))
  1110.                 continue
  1111.             if len(list) == 2 and list[0] <= list[1]:
  1112.                 new.append((list[0], list[1]))
  1113.                 continue
  1114.             raise ValueError, 'bad data passed to IntSet'
  1115.         
  1116.         self.pairs = self.pairs + new
  1117.         self.normalize()
  1118.  
  1119.  
  1120.  
  1121. def pickline(file, key, casefold = 1):
  1122.     
  1123.     try:
  1124.         f = open(file, 'r')
  1125.     except IOError:
  1126.         return None
  1127.  
  1128.     pat = re.escape(key) + ':'
  1129.     if casefold:
  1130.         pass
  1131.     prog = re.compile(pat, re.IGNORECASE)
  1132.     while None:
  1133.         line = f.readline()
  1134.         if not line:
  1135.             break
  1136.         
  1137.         if prog.match(line):
  1138.             text = line[len(key) + 1:]
  1139.             while None:
  1140.                 line = f.readline()
  1141.                 if not line or not line[0].isspace():
  1142.                     break
  1143.                 
  1144.                 text = text + line
  1145.                 continue
  1146.                 return text.strip()
  1147.                 continue
  1148.                 return None
  1149.  
  1150.  
  1151. def updateline(file, key, value, casefold = 1):
  1152.     
  1153.     try:
  1154.         f = open(file, 'r')
  1155.         lines = f.readlines()
  1156.         f.close()
  1157.     except IOError:
  1158.         lines = []
  1159.  
  1160.     pat = re.escape(key) + ':(.*)\n'
  1161.     if casefold:
  1162.         pass
  1163.     prog = re.compile(pat, re.IGNORECASE)
  1164.     if value is None:
  1165.         newline = None
  1166.     else:
  1167.         newline = '%s: %s\n' % (key, value)
  1168.     for i in range(len(lines)):
  1169.         line = lines[i]
  1170.         if prog.match(line):
  1171.             if newline is None:
  1172.                 del lines[i]
  1173.             else:
  1174.                 lines[i] = newline
  1175.             break
  1176.             continue
  1177.     elif newline is not None:
  1178.         lines.append(newline)
  1179.     
  1180.     tempfile = file + '~'
  1181.     f = open(tempfile, 'w')
  1182.     for line in lines:
  1183.         f.write(line)
  1184.     
  1185.     f.close()
  1186.     os.rename(tempfile, file)
  1187.  
  1188.  
  1189. def test():
  1190.     global mh, f, f
  1191.     os.system('rm -rf $HOME/Mail/@test')
  1192.     mh = MH()
  1193.     
  1194.     def do(s):
  1195.         print s
  1196.         print eval(s)
  1197.  
  1198.     do('mh.listfolders()')
  1199.     do('mh.listallfolders()')
  1200.     testfolders = [
  1201.         '@test',
  1202.         '@test/test1',
  1203.         '@test/test2',
  1204.         '@test/test1/test11',
  1205.         '@test/test1/test12',
  1206.         '@test/test1/test11/test111']
  1207.     for t in testfolders:
  1208.         do('mh.makefolder(%r)' % (t,))
  1209.     
  1210.     do("mh.listsubfolders('@test')")
  1211.     do("mh.listallsubfolders('@test')")
  1212.     f = mh.openfolder('@test')
  1213.     do('f.listsubfolders()')
  1214.     do('f.listallsubfolders()')
  1215.     do('f.getsequences()')
  1216.     seqs = f.getsequences()
  1217.     seqs['foo'] = IntSet('1-10 12-20', ' ').tolist()
  1218.     print seqs
  1219.     f.putsequences(seqs)
  1220.     do('f.getsequences()')
  1221.     for t in reversed(testfolders):
  1222.         do('mh.deletefolder(%r)' % (t,))
  1223.     
  1224.     do('mh.getcontext()')
  1225.     context = mh.getcontext()
  1226.     f = mh.openfolder(context)
  1227.     do('f.getcurrent()')
  1228.     for seq in ('first', 'last', 'cur', '.', 'prev', 'next', 'first:3', 'last:3', 'cur:3', 'cur:-3', 'prev:3', 'next:3', '1:3', '1:-3', '100:3', '100:-3', '10000:3', '10000:-3', 'all'):
  1229.         
  1230.         try:
  1231.             do('f.parsesequence(%r)' % (seq,))
  1232.         except Error:
  1233.             msg = None
  1234.             print 'Error:', msg
  1235.  
  1236.         stuff = os.popen('pick %r 2>/dev/null' % (seq,)).read()
  1237.         list = map(int, stuff.split())
  1238.         print list, '<-- pick'
  1239.     
  1240.     do('f.listmessages()')
  1241.  
  1242. if __name__ == '__main__':
  1243.     test()
  1244.  
  1245.